Curso de Python para Ingenieros Mecánicos

Por: Eduardo Vieira



Programación básica en Python y el Jupyter Notebook


Markdown

Texto en cursiva, negrita o cursiva negrita.

Puedes crear listas numeradas o sin numerar:

  • Un elemento sun numerar
    • Sub elemento
      • Sub sub elemento
        • Sub elemento
      • Una cosa
      • Otra cosa
  • Segundo elemento sin numerar
    • Sub elemento
  • Tercero
    • Sub elemento

Numerada:

  1. Punto uno
    1. Sub punto uno
    2. Sub punto dos
  2. Punto dos
  3. Punto tres

Super linea horizontal


Una cita (blockquote)

Hermoso es mejor que feo. Explícito es mejor que implícito. Simple es mejor que complejo. Complejo es mejor que complicado. Plano es mejor que anidado. Escaso es mejor que denso. Cuenta la legibilidad. Los casos especiales no son lo suficientemente especial como para romper las reglas. Aunque sentido práctico supera pureza. Los errores nunca debe pasar en silencio. A menos que explícitamente silenciados. Ante la ambigüedad, rechaza la tentación de adivinar. Debería haber una - y preferiblemente sólo una - manera obvia de hacerlo. Aunque esa manera puede no ser obvia al principio a menos que seas holandés. Ahora es mejor que nunca. Aunque nunca es a menudo mejor que la justo ahora. Si la implementación es difícil de explicar, es una mala idea. Si la implementación es fácil de explicar, puede ser una buena idea. Namespaces son una gran idea de fanfarria - Vamos a hacer más de esos!

Zen de Python

Enlaces:

Google

Títulos:

Título 1

Título 2

Titulo 3

Código

Puedes escribir código que no se ejecutará:

def f(x):
    """Calcula el cuadrado de un número"""
    return x**2

O

def f(x):
        """Calcula el cuadrado de un número"""
        return x**2

Otro ejemplo en C

if (i=0; i<n; i++) {
  printf("hello %d\n", i);
  x += 4;
}

O

if (i=0; i<n; i++) {
      printf("hello %d\n", i);
      x += 4;
    }

Bonitas ecuaciones en Latex

Gracias a MathJax, podemos icluir fómulas escritas en Latex como
$e^{i\pi} + 1 = 0$ o

$$e^x=\sum_{i=0}^\infty \frac{1}{i!}x^i$$

Las ecuaciones en linea ven entre $:

$e^{i\pi} + 1 = 0$

Expresiones en su propia linea van entre $$:

$$e^x=\sum_{i=0}^\infty \frac{1}{i!}x^i$$

Tablas

Esta es
una tabla
$\theta$ $x^2$

HTML

tambien podemos escribir en HTML

Header 1 Header 2
row 1, cell 1 row 1, cell 2
row 2, cell 1 row 2, cell 2

Imagenes y videos

Imagen:

o con HTML

Video

Introducción a la programación en Python

Variables y tipos

Nombres de símbolos

Los nombres de las variables en Python pueden contener los caracteres a-z, A-Z, 0-9 y algunos caracteres especiales como _. Los nombres de variables normales deben comenzar con una letra.

Por convención, los nombres de las variables comienzan con letra minúscula, mientras que los nombres de las clases comienzan con una letra mayúscula.

Además, existen algunos palabras claves Python que no pueden ser usados como nombres de variables. Éstas son:

and, as, assert, break, class, continue, def, del, elif, else, except, 
exec, finally, for, from, global, if, import, in, is, lambda, not, or,
pass, print, raise, return, try, while, with, yield

Nota: Atención con la palabra lambda, que podría fácilmente ser un nombre de variable natural en un programa científico. Sin embargo, como es una palabra clave, no puede ser usado como nombre de una variable.

Asignaciones

El operador para asignar valores en Python es el signo igual (=). Python es un lenguage de escritura dinámica, de modo que no necesitamos especificar el tipo de una variable cuando la creamos.

Al asignar un valor a una variable nueva se crea esa variable:


In [4]:
# asignaciones de variables
x = 1.0
mi_variable = 12.2

Aunque no se especifique explícitamente, cada variable sí tiene un tipo asociada a ella. El tipo es extraido del valor que le fue asignado.


In [5]:
type(x)


Out[5]:
float

Si asignamos un nuevo valor a una variable, su tipo puede cambiar.


In [6]:
x = 1

In [7]:
type(x)


Out[7]:
int

Si tratamos de unsar una variable que no ha sido definida obtenemo un mensaje de error (NameError):


In [8]:
print(y)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-8-36b2093251cd> in <module>()
----> 1 print(y)

NameError: name 'y' is not defined

Tipos Fundamentales


In [9]:
# enteros
x = 1
type(x)


Out[9]:
int

In [10]:
# flotantes
x = 1.0
type(x)


Out[10]:
float

In [11]:
# booleanos
b1 = True
b2 = False

type(b1)


Out[11]:
bool

In [12]:
# números complejos: note que se usa `j` para especificar la parte imaginaria
x = 1.0 - 1.0j
type(x)


Out[12]:
complex

In [13]:
print(x)


(1-1j)

In [14]:
print(x.real, x.imag)


1.0 -1.0

Funciones de relacionadas con Tipos

El módulo types contiene definiciones de nombres de tipo que pueden ser usadas para testear si las variables son de un cierto tipo:


In [15]:
import types

# imprime todos los tipos definidos en el módulo `types`
print(dir(types))


['AsyncGeneratorType', 'BuiltinFunctionType', 'BuiltinMethodType', 'CodeType', 'CoroutineType', 'DynamicClassAttribute', 'FrameType', 'FunctionType', 'GeneratorType', 'GetSetDescriptorType', 'LambdaType', 'MappingProxyType', 'MemberDescriptorType', 'MethodType', 'ModuleType', 'SimpleNamespace', 'TracebackType', '_GeneratorWrapper', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_ag', '_calculate_meta', '_collections_abc', '_functools', 'coroutine', 'new_class', 'prepare_class']

In [16]:
x = 1.0

# verifica si la variable x es flotante
type(x) is float


Out[16]:
True

In [17]:
# verifica si la variable x es un entero
type(x) is int


Out[17]:
False

Podemos también usar el método isinstance para testear tipos de variables:


In [18]:
isinstance(x, float)


Out[18]:
True

Conversión de Tipo


In [19]:
x = 1.5

print(x, type(x))


1.5 <class 'float'>

In [20]:
x = int(x)

print(x, type(x))


1 <class 'int'>

In [21]:
z = complex(x)

print(z, type(z))


(1+0j) <class 'complex'>

In [22]:
x = float(z)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-22-e719cc7b3e96> in <module>()
----> 1 x = float(z)

TypeError: can't convert complex to float

Un número complejo no puede ser convertido a un número flotante o a un entero. Necesitamos usar z.real, o bien z.imag, para extraer la parte que deseamos del número complejo z:


In [23]:
y = bool(z.real)

print(z.real, " -> ", y, type(y))

y = bool(z.imag)

print(z.imag, " -> ", y, type(y))


1.0  ->  True <class 'bool'>
0.0  ->  False <class 'bool'>

Operadores y comparaciones

La mayoría de los operadores y las comparaciones en Python funcionan como one esperaría:

  • Operadores aritméticos +, -, *, /, // (división entera), '**' potencia

In [24]:
1 + 2, 1 - 2, 1 * 2, 1 / 2


Out[24]:
(3, -1, 2, 0.5)

In [25]:
1.0 + 2.0, 1.0 - 2.0, 1.0 * 2.0, 1.0 / 2.0


Out[25]:
(3.0, -1.0, 2.0, 0.5)

In [26]:
# División entera de dos númenos flotantes
3.0 // 2.0


Out[26]:
1.0

In [27]:
# Atención! El operador de potencia en Python no es ^, sino **
2 ** 2


Out[27]:
4
  • Los operadores booleanos se escriben como palabras: and, not, or.

In [28]:
True and False


Out[28]:
False

In [29]:
not False


Out[29]:
True

In [30]:
True or False


Out[30]:
True
  • Operadores de comparación >, <, >= (mayor o igual), <= (menor o igual), == igualdad, es identico.

In [31]:
2 > 1, 2 < 1


Out[31]:
(True, False)

In [32]:
2 > 2, 2 < 2


Out[32]:
(False, False)

In [33]:
2 >= 2, 2 <= 2


Out[33]:
(True, True)

In [34]:
# igualdad
[1,2] == [1,2]


Out[34]:
True

In [35]:
# ¿objetos identicos?
l1 = l2 = [1,2]

l1 is l2


Out[35]:
True

Tipos compuestos: Cadenas, listas y diccionarios

Cadenas

Las cadenas son el tipo de variables que es usado para almacenar mensajes de texto.


In [36]:
s = "Hola mundo"
type(s)


Out[36]:
str

In [37]:
# longitud de la cadena: el número de caracteres que contiene
len(s)


Out[37]:
10

In [38]:
# reemplaza una subcadena de una cadena por cadena
s2 = s.replace("mundo", "universo")
print(s2)


Hola universo

Podemos aislar un carácter en una cadena usando []:


In [39]:
s[0]


Out[39]:
'H'

Atención usuarios de MATLAB: el indexado comienza en 0!

Podemos extraer una parte de una cadena usando la sintaxis [desde:hasta], que extrae caracteres entre los índices desde y hasta:


In [40]:
s[0:5]


Out[40]:
'Hola '

Si omitimos desde o bien hasta de [desde:hasta], por defecto se entiende que se refiere al comienzo y/o al fin de la cadena, respectivamente:


In [41]:
s[:5]


Out[41]:
'Hola '

In [42]:
s[6:]


Out[42]:
'undo'

In [43]:
s[:]


Out[43]:
'Hola mundo'

Podemos también definir el tamaño del paso usando la sintaxis [desde:hasta:paso] (el valor por defecto de paso es 1, como ya vismo):


In [44]:
s[::1]


Out[44]:
'Hola mundo'

In [45]:
s[::2]


Out[45]:
'Hl ud'

Esta técnica es llamada slicing ("rebanado"). Puede leer más sobre la sintaxis aquí http://pyspanishdoc.sourceforge.net/lib/built-in-funcs.html y aquí (en inglés) http://docs.python.org/release/2.7.3/library/functions.html?highlight=slice#slice

Python tiene un rico conjunto de funciones para procesar texto. Ver por ejemplo http://docs.python.org/2/library/string.html (en inglés) para más información.

Ejemplos de formateo de cadenas


In [46]:
print("uno", "dos", "tres")  # El comando print puede desplegar varias cadenas


uno dos tres

In [47]:
print("uno", 1.0, False, -1j)  # El comendo print convierte todos los argumentos a cadenas


uno 1.0 False (-0-1j)

In [48]:
print("uno" + "dos" + "tres") # cadenas "sumadas" con + son contatenadas sin espacio entre ellas


unodostres

In [49]:
print("valor = %f" % 1.0)       # podemos usar formateo de cadenas en el estilo del lenguaje C


valor = 1.000000

In [50]:
# este formateo crea una cadena
s2 = "valor1 = %.2f. valor2 = %d" % (3.1415, 1.5)

print(s2)


valor1 = 3.14. valor2 = 1

In [51]:
# forma alternativa, más intuitiva para formatear una cadena
s3 = 'valor1 = {0}, valor2 = {1}'.format(3.1415, 1.5)

print(s3)


valor1 = 3.1415, valor2 = 1.5

Listas

Listas son muy similares a las cadenas, excepto que cada elemento puede ser de un tipo diferente.

La sintaxis para crear listas en Python es [..., ..., ...]:


In [52]:
l = [1,2,3,4]

print(type(l))
print(l)


<class 'list'>
[1, 2, 3, 4]

Podemos usar las mismas técnicas de "rebanado" que usamos en el caso de cadenas para manipular listas:


In [53]:
print(l)

print(l[1:3])

print(l[::2])


[1, 2, 3, 4]
[2, 3]
[1, 3]

Atención usuarios de MATLAB: el indexado comienza en 0!


In [54]:
l[0]


Out[54]:
1

Los elementos en una lista no requieren ser del mismo tipo:


In [55]:
l = [1, 'a', 1.0, 1-1j]

print(l)


[1, 'a', 1.0, (1-1j)]

Las listas en Python pueden ser inhomogéneas y arbitrariamente anidadas:


In [56]:
lista_anidada = [1, [2, [3, [4, [5]]]]]

lista_anidada


Out[56]:
[1, [2, [3, [4, [5]]]]]

Las listas juegan un rol muy importante en Python y son, por ejemplo, usadas en bucles y otras estructuras de control de flujo (discutidas más abajo). Existan muchas funciones convenientes para generar listas de varios tipos, por ejemplo la función range:


In [57]:
desde = 10
hasta = 30
paso = 2

range(desde, hasta, paso)


Out[57]:
range(10, 30, 2)

In [58]:
# en Python 3 range genera un interador, que puede ser convertido a una lista usando 'list(...)'. Esto no tiene efecto en Python 2
list(range(desde, hasta, paso))


Out[58]:
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]

In [59]:
list(range(-10, 10))


Out[59]:
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [60]:
s


Out[60]:
'Hola mundo'

In [61]:
# convierte una cadena a una lista, por conversión de tipo:

s2 = list(s)

s2


Out[61]:
['H', 'o', 'l', 'a', ' ', 'm', 'u', 'n', 'd', 'o']

In [62]:
# ordenando listas
s2.sort()

print(s2)


[' ', 'H', 'a', 'd', 'l', 'm', 'n', 'o', 'o', 'u']

Agregando, insertando, modificando, y removiendo elementos de listas


In [63]:
# crea una nueva lista vacía
l = []

# agrega un elemento usando `append`
l.append("A")
l.append("d")
l.append("d")

print(l)


['A', 'd', 'd']

Podemos modificar listas asignando nuevos valores a los elementos de la lista. En lenguaje técnico se dice que la lista es mutable.


In [64]:
l[1] = "p"
l[2] = "p"

print(l)


['A', 'p', 'p']

In [65]:
l[1:3] = ["d", "d"]

print(l)


['A', 'd', 'd']

Insertar un elemento en una posición específica insert


In [66]:
l.insert(0, "i")
l.insert(1, "n")
l.insert(2, "s")
l.insert(3, "e")
l.insert(4, "r")
l.insert(5, "t")

print(l)


['i', 'n', 's', 'e', 'r', 't', 'A', 'd', 'd']

Eliminar el primer elemento con un valor específico usando 'remove'


In [67]:
l.remove("A")

print(l)


['i', 'n', 's', 'e', 'r', 't', 'd', 'd']

Eliminar un elemento en una posición específica usando del:

Puede introducir help(list) para más detalles, o leer la documentación en la red

Tuplas

Tuplas son similares a las listas, excepto que ellas no pueden ser modificadas una vez creadas, es decir, son inmutables.

En Python, las tuplas son creadas usando la sintaxis (..., ..., ...), o incluso ..., ...:


In [68]:
punto = (10, 20)

print(punto, type(punto))


(10, 20) <class 'tuple'>

In [69]:
punto = 10, 20

print(punto, type(punto))


(10, 20) <class 'tuple'>

Podemos separar una tupla asignandola a una lista de variables separadas por coma:


In [70]:
x, y = punto

print("x =", x)
print("y =", y)


x = 10
y = 20

Si intentamos asignar un nuevo valor a un elemento de una tupla obtenemos un error:


In [71]:
punto[0] = 20


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-71-4e812ab96337> in <module>()
----> 1 punto[0] = 20

TypeError: 'tuple' object does not support item assignment

Dictionarios

Dictionarios son también como listas, excepto que cada elemento es un par clave-valor. La sintaxsis de los diccionarios es {clave1 : valor1, ...}:


In [72]:
parametros = {"parametro1" : 1.0,
          "parametro2" : 2.0,
          "parametro3" : 3.0,}

print(type(parametros))
print(parametros)


<class 'dict'>
{'parametro1': 1.0, 'parametro2': 2.0, 'parametro3': 3.0}

In [73]:
print("parametro1 = " + str(parametros["parametro1"]))
print("parametro2 = " + str(parametros["parametro2"]))
print("parametro3 = " + str(parametros["parametro3"]))


parametro1 = 1.0
parametro2 = 2.0
parametro3 = 3.0

In [74]:
parametros["parametro1"] = "A"
parametros["parametro2"] = "B"

# agrega una nueva entrada
parametros["parametro4"] = "D"

print("parametro1 = " + str(parametros["parametro1"]))
print("parametro2 = " + str(parametros["parametro2"]))
print("parametro3 = " + str(parametros["parametro3"]))
print("parametro4 = " + str(parametros["parametro4"]))


parametro1 = A
parametro2 = B
parametro3 = 3.0
parametro4 = D

Control de flujo

Sentencias condicionales: if, elif, else

La sintaxis Python para la ejecución condicional de código usa las palabras clave if, elif (else if), else:


In [75]:
afirmacion1 = False
afirmacion2 = False

if afirmacion1:
    print("afirmacion1 es verdadera")
    
elif afirmacion2:
    print("afirmacion2 es verdadera")
    
else:
    print("afirmacion1 y afirmacion2 son falsas")


afirmacion1 y afirmacion2 son falsas

Aquí encontramos por primera un aspecto pecular e inusual del lenguaje Python: Los bloques del programa son definidos por su nivel de indentación (la cantidad de espacio antes de cada linea).

Compare con el código equivalente en C:

if (afirmacion1)
{
    printf("afirmacion1 es verdadera\n");
}
else if (afirmacion2)
{
    printf("afirmacion1 es verdadera\n");
}
else
{
    printf("afirmacion1 y afirmacion2 son falsas\n");
}

En C los bloques son definidos por los paréntesis llaves { y }. El nivel de indentación (espacio en blanco antes del código) no importa (es completamente opcional).

En Python, la extensión de un bloque de código es definido por el nivel de indentación (usualmente un tab o cuatro espacios en blanco). Esto significa que debemos ser cuidados@s de indentar nuestro código correctamente, de lo contrario tendremos errores de sintaxis.

Ejemplos:


In [76]:
afirmacion1 = afirmacion2 = True

if afirmacion1:
    if afirmacion2:
        print("tanto afirmacion1 como afirmacion2 son verdaderas")


tanto afirmacion1 como afirmacion2 son verdaderas

In [77]:
# Mala indentación!
if afirmacion1:
    if afirmacion2:
        print("tanto afirmacion1 como afirmacion2 son verdaderas")  # esta línea está mal indentada


tanto afirmacion1 como afirmacion2 son verdaderas

In [78]:
afirmacion1 = False 

if afirmacion1:
    print("afirmacion1 es verdadera")
    
    print("aun estamos dentro del bloque if")

In [79]:
if afirmacion1:
    print("afirmacion1 es verdadera")
    
print("ahora estamos fuera del bloque")


ahora estamos fuera del bloque

Ciclos

En Python, los ciclos (loops) puede ser programados de varias maneras diferentes. La forma más común es usando un cicle for, que se usa junto con objetos iterables, como por ejemplos las listas. La sintaxis básica es:

Ciclos for:


In [80]:
for x in [1,2,3]:
    print(x)


1
2
3

El ciclo for itera sobre los elementos de la lista suministrada y ejecuta el bloque suministrado una vez para cada elemento. Cualquier tipo de lista puede ser usada para un ciclo for. Por ejemplo:


In [81]:
for x in range(4): # por defecto range comienza con 0
    print(x)


0
1
2
3

Nota: range(4) no incluye el 4 !


In [82]:
for x in range(-3,3):
    print(x)


-3
-2
-1
0
1
2

In [83]:
for palabra in ["computación", "científica", "con", "Python"]:
    print(palabra)


computación
científica
con
Python

Para iterar sobre pares clave-valor en un diccionario:


In [84]:
for clave, valor in parametros.items():
    print(clave + " = " + str(valor))


parametro1 = A
parametro2 = B
parametro3 = 3.0
parametro4 = D

Algunas veces es útil tener acceso a los índices de los valores mientras se itera sobre una lista. Podemos usar la función enumerate para esto:


In [85]:
for idx, x in enumerate(range(-3,3)):
    print(idx, x)


0 -3
1 -2
2 -1
3 0
4 1
5 2

Listas: Creando listas usando ciclos for:

Una forma conveniente y compacta de inicializar listas:


In [86]:
l1 = [x**2 for x in range(0,5)]

print(l1)


[0, 1, 4, 9, 16]

Ciclos while:


In [87]:
i = 0

while i < 5:
    print(i)    
    i = i + 1
    
print("listo")


0
1
2
3
4
listo

Note que el comando print("listo") no es parte del cuerpo del ciclo while, debido a su indentación.

Funciones

En Python una función es definida usando la palabra clave def, seguida de un nombre para la función, una variable entre paréntesis (), y el símbolo de dos puntos :. El siguiente código, con un nivel adicional de indentación, is el cuerpo de la función.


In [88]:
def func0():   
    print("test")

In [89]:
func0()


test

En forma opcional, pero muy recomendada, podemos definir un "docstring", que es una descripción del propósito y comportamiento de la función. El docstring debería ser incluido directamente después de la definición de la función, antes del código en el cuerpo de la función.


In [90]:
def func1(s):
    """
    Imprime la cadena 's' y dice cuántos caracteres tiene
    """
    
    print(s + " tiene " + str(len(s)) + " caracteres")

In [91]:
help(func1)


Help on function func1 in module __main__:

func1(s)
    Imprime la cadena 's' y dice cuántos caracteres tiene


In [92]:
func1("test")


test tiene 4 caracteres

Funciones que retornan un valor usan la palabra clave return:


In [93]:
def cuadrado(x):
    """
    Calcula el cuadrado de x.
    """
    return x**2

In [94]:
cuadrado(4)


Out[94]:
16

Podemos retornar múltiples valores desde una función usando las tuplas (ver más arriba):


In [96]:
def potencias(x):
    """
    Calcula algunas potencias de x.
    """
    return x**2, x**3, x**4

In [97]:
potencias(3)


Out[97]:
(9, 27, 81)

In [98]:
x2, x3, x4 = potencias(3)

print(x3)


27

Argumentos por defecto y argumentos de palabra clave

En la definición de una función, podemos asignar valores por defecto a los argumentos de la función:


In [99]:
def mifunc(x, p=2, debug=False):
    if debug:
        print("evaluando mifunc para x = " + str(x) + " usando el exponente p = " + str(p))
    return x**p

Si no suministramos un valor para el argumento debug al llamar a la función mifunc se considera el valor definido por defecto:


In [100]:
mifunc(5)


Out[100]:
25

In [101]:
mifunc(5, debug=True)


evaluando mifunc para x = 5 usando el exponente p = 2
Out[101]:
25

Si listamos explícitamente el nombre de los argumentos al llamar una función, ellos no necesitan estar en el mismo orden usando en la definición de la función. Esto es llamado argumentos de palabra clave (keyword), y son a menudo muy útiles en funciones que requieren muchos argumentos opcionales.


In [102]:
mifunc(p=3, debug=True, x=7)


evaluando mifunc para x = 7 usando el exponente p = 3
Out[102]:
343

Funciones sin nombre (funciones lambda)

En Python podemos también crear funciones sin nombre, usando la palabra clave lambda:


In [103]:
f1 = lambda x: x**2
    
# es equivalente a 

def f2(x):
    return x**2

In [104]:
f1(2), f2(2)


Out[104]:
(4, 4)

Esta técnica es útil, por ejemplo, cuando queremos pasar una función simple como argumento de otra función, como en este caso:


In [105]:
# map es una función predefinida en Python
map(lambda x: x**2, range(-3,4))


Out[105]:
<map at 0x7fe9df1ffd68>

In [106]:
# in Python 3 podemos usar `list(...)` para convertir la iteración a una lista explícita
list(map(lambda x: x**2, range(-3,4)))


Out[106]:
[9, 4, 1, 0, 1, 4, 9]

Clases

Las clases son una característica clave de la programación orientada al objeto. Una clase es una estructura para representar un objeto y las operaciones que pueden ser realizadas sobre el objeto.

En Python una clase puede contener atributos (variables) y métodos (funciones).

En Python una clase es definida casi como una función, pero usando la palabra clave class, y la definición de la clase usualmente contiene algunas definiciones de métodos (una función en una clase).

  • Cada método de una clase debería tener un argumento self como su primer argumento. Este objeto es una autoreferencia.

  • Algunos nombres de métodos de clases tienen un significado especial, por ejemplo:


In [107]:
class Punto:
    """
    Clase simple para representar un punto en un sistema de coordenadas cartesiano.
    """
    
    def __init__(self, x, y):
        """
        Crea un nuevo punto en x, y.
        """
        self.x = x
        self.y = y
        
    def traslada(self, dx, dy):
        """
        Traslada el punto en dx y dy en las direcciones x e y respectivamente.
        """
        self.x += dx
        self.y += dy
        
    def __str__(self):
        return("Punto en [%f, %f]" % (self.x, self.y))

Para crear una nuva instancia de una clase:


In [108]:
p1 = Punto(0, 0) # eso invoca el método __init__ en la cláse Punto

print(p1)         # esto invoca el método __str__


Punto en [0.000000, 0.000000]

Para invocar un método en la instancia de clase p:


In [109]:
p2 = Punto(1, 1)

p1.traslada(0.25, 1.5)

print(p1)
print(p2)


Punto en [0.250000, 1.500000]
Punto en [1.000000, 1.000000]

Note que llamar a métodos de clases puede modificar el estado de esa instancia de clase particular, pero no afecta otras instancias de la clase o alguna otra variable global.

Esto es una de las cosas buenas de un diseño orientado al objeto: código como las funciones y variables relacionadas son agrupadas en entidades separadas e independientes.

Módulos

La mayoría de la funcionalidad en Python es provista por módulos. La Librería Estándar de Python es una gran colección de módulos que proveen implementaciones multiplataforma de recursos tales como el acceso al sistema operativo, entrada/salido de archivos (file I/O), manejo de cadenas, comunicación en redes, y mucho más.

Para usar un módulo en un programa Python éste debe primero ser importado. Un módulo puede ser importado usando el comando import. Por ejemplo, para importar el módulo math, que contiene muchas funciones matemáticas estándar, podemos usar:


In [110]:
import math

Esto incluye el módulo completo y lo deja disponible para su uso en el programa. Por ejemplo, podemos escribir:


In [111]:
import math

x = math.cos(2 * math.pi)

print(x)


1.0

Alternativamente, podemos elegir importar todos los símbolos (funciones y variables) en un módulo al espacio de nombres (namespace) actual (de modo que no necesitemos usar el prefijo "math." cada vez que usemos algo del módulo math:


In [112]:
from math import *

x = cos(2 * pi)

print(x)


1.0

Esta forma de proceder puede ser muy conveniente, pero en programas largos que incluyen muchos módulos es a menudo una buena idea mantener los símbolos de cada módulo en sus propios espacios de nombres, usando import math. Esto elimina potenciales confusiones con eventuales colisiones de nombres.

Como una tercera alternativa, podemos importar sólo algunos símbolos seleccionados desde un módulo listando explícitamente aquellos símbolos que deseamos importar, en lugar de usar el carácter comodín *:


In [113]:
from math import cos, pi

x = cos(2 * pi)

print(x)


1.0

Mirando qué contiene un módulo, y su documentación

Luego que se ha cargado un módulo, podemos listar los símbolos que éste provee usando la función dir:


In [114]:
import math

dir(math)


Out[114]:
['__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'acos',
 'acosh',
 'asin',
 'asinh',
 'atan',
 'atan2',
 'atanh',
 'ceil',
 'copysign',
 'cos',
 'cosh',
 'degrees',
 'e',
 'erf',
 'erfc',
 'exp',
 'expm1',
 'fabs',
 'factorial',
 'floor',
 'fmod',
 'frexp',
 'fsum',
 'gamma',
 'gcd',
 'hypot',
 'inf',
 'isclose',
 'isfinite',
 'isinf',
 'isnan',
 'ldexp',
 'lgamma',
 'log',
 'log10',
 'log1p',
 'log2',
 'modf',
 'nan',
 'pi',
 'pow',
 'radians',
 'sin',
 'sinh',
 'sqrt',
 'tan',
 'tanh',
 'tau',
 'trunc']

Usando la función help podemos obtener una descripción de cada función (casi... no todas las funciones tienen docstrings, como se les llama técnicamente. Sin embargo, la mayoría de las funciones están documentadas de esta forma).


In [115]:
help(math.log)


Help on built-in function log in module math:

log(...)
    log(x[, base])
    
    Return the logarithm of x to the given base.
    If the base not specified, returns the natural logarithm (base e) of x.


In [116]:
log(10) # calcula el logaritmo de 10 en base e


Out[116]:
2.302585092994046

In [117]:
log(10, 2) # calcula el logaritmo de 10 en base 2


Out[117]:
3.3219280948873626

También podemos usar la función help directamente sobre los módulos:

help(math) 

Algunos módulos muy útiles de la librería estándar de Python son os (interfaz con el sistema operativo), sys (Parámetros y funciones específicas del sistema), math (funciones matem'aticas), shutil (operaciones con archivos), subprocess, multiprocessing, threading.

Una lista completa de los módulos estándar para Python 2 y Python 3 está disponible (en inglés) en http://docs.python.org/2/library/ y http://docs.python.org/3/library/, respectivamente. Una versión en español está disponible en http://pyspanishdoc.sourceforge.net/lib/lib.html.

Creación de Módulos

Uno de los conceptos más importantes en programación es el de reusar código para evitar repeticiones.

La idea es escribir funciones y clases con un propósito y extensión bien definidos, y reusarlas en lugar de repetir código similar en diferentes partes del programa (programación modular). Usualmente el resultado es que se mejora ostensiblemente la facilidad de lectura y de mantención de un programa. En la práctica, esto significa que nuestro programa tendrá menos errores, y serán más fáciles de extender y corregir.

Python permite programación modular en diferentes niveles. Las funciones y las clases son ejemplos de herramientas para programación modular de bajo nivel. Los módulos Python son construcciones de programación modular de más alto nivel, donde podemos colectar variables relacionadas, funciones y clases. Un módulo Python es definido en un archivo Python (con extensión .py), y puede ser accequible a otros módulos Python y a programas usando el comendo import.

Considere el siguiente ejemplo: el archivo mimodulo.py contiene una implementación simple de una variable, una función y una clase:


In [119]:
%more mimodulo.py

Podemos importar el módulo mimodulo a un programa Python usando import:


In [120]:
import mimodulo

Use help(module) para obtener un resumen de lo que suministra el módulo:


In [121]:
help(mimodulo)


Help on module mimodulo:

NAME
    mimodulo

DESCRIPTION
    Ejemplo de un módulo Python. Contiene una variable llamada mi_variable,
    una función llamada mi_function, y una clase llamada MiClase.

CLASSES
    builtins.object
        MiClase
    
    class MiClase(builtins.object)
     |  Clase ejemplo.
     |  
     |  Methods defined here:
     |  
     |  __init__(self)
     |      Initialize self.  See help(type(self)) for accurate signature.
     |  
     |  get_variable(self)
     |  
     |  set_variable(self, nuevo_valor)
     |      Asigna self.variable a un nuevo valor
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)

FUNCTIONS
    mi_function()
        Función ejemplo

DATA
    mi_variable = 0

FILE
    /home/eduardo/Documentos/Curso Python/mimodulo.py



In [122]:
mimodulo.mi_variable


Out[122]:
0

In [123]:
mimodulo.mi_function()


Out[123]:
0

In [124]:
mi_clase = mimodulo.MiClase() 
mi_clase.set_variable(10)
mi_clase.get_variable()


Out[124]:
10

Excepciones

En Python los errores son manejados con una construcción especial de lenguaje llamada "Exceptions" (excepciones). Cuando ocurre un error, una excepción puede ser hecha, que interrumpe el flujo normal del programa y retorna a algún otro lugar del código donde se definan los comandos try-except más cercanos.

Para generar una excepción podemos usar el comando raise, que toma un argumento que debe ser una instancia de la clase BaseExpection o una clase derivada de ella.


In [126]:
raise Exception("descripción del error")


---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-126-ae92a99eabb1> in <module>()
----> 1 raise Exception("descripción del error")

Exception: descripción del error

Un úso típico de las excepciones es para abortar funciones cuando ocurre algún error, por ejemplo:

def mi_funcion(argumentos):

    if not verify(argumentos):
        raise Expection("Argumentos invalidos")

    # el resto del código sigue aquí

Para capturar los errores que son generados por funciones y métodos de clases, o por el mismo intérprete Python, use los comandos try y except:

try:
    # aquí va el código normal
except:
    # el código para manejar el error va aquí
    # Este código no se ejecuta a menos que 
    # el código de arriba genere un error

Por ejemplo:


In [127]:
try:
    print("test")
    # genera un error: ya que la variable test no está definida
    print(test)
except:
    print("Encontré una excepción")


test
Encontré una excepción

Para obtener información sobre un error, podemos accesar la instancia de clase Exception que describe la excepción usando por ejemplo:

except Exception as e:

In [128]:
try:
    print("test")
    # genera un error: ya que la variable test no está definida
    print(test)
except Exception as e:
    print("Encontré una excepción:" + str(e))


test
Encontré una excepción:name 'test' is not defined

Lectura adicional


In [129]:
# Esta celda da el estilo al notebook
from IPython.core.display import HTML
css_file = './css/aeropython.css'
HTML(open(css_file, "r").read())


Out[129]:
/* This template is inspired in the one used by Lorena Barba in the numerical-mooc repository: https://github.com/numerical-mooc/numerical-mooc We thank her work and hope you also enjoy the look of the notobooks with this style */ El estilo se ha aplicado =)